/**
* @file app_cluster_level.c
* @brief Common process - adapter the flash api
* @version 0.1
* @date 2021-10-22
*
* @copyright Copyright 2020-2021 Tuya Inc. All Rights Reserved.
*
*/
#include "tal_log.h"
#include "tal_system.h"
#include "tal_memory.h"
#include "tal_attribute_rw.h"
#include "app_handle.h"
#include "app_cluster_level.h"
#include "app_cluster_on_off.h"

#define STEP_LEVEL_UP 0
#define MAX_MOVE_RATE 1000

/**
 * @note set tuya private bright cmd handler
 * @param [in] {ZIGBEE_CMD_T} cmd_type
 * @param [in] {USHORT_T } bright (0-1000)
 * @return: none
 */
STATIC VOID_T __app_level_ty_cmd_set_bright_handler(ZIGBEE_CMD_T cmd_type, USHORT_T bright)
{
    UCHAR_T onoff = app_light_ctrl_data_switch_get();
    TAL_SEND_QOS_E send_qos = QOS_VIP_0;
    USHORT_T delay_ms = 0;

    if (cmd_type == ZIGBEE_CMD_GROUP)
    {

        delay_ms = 10000 + tal_system_get_random(1000);
        send_qos = QOS_0;
    }
    TAL_PR_DEBUG("Receive bright : %d", bright);
    app_level_report_ty_bright_value(send_qos, delay_ms, bright);
    app_light_ctrl_data_bright_set(bright, 5);
    app_handle_start(TRUE);

    if (onoff != app_light_ctrl_data_switch_get())
    {
        //updata onoff attr
        tal_zg_write_attribute(1,
                               CLUSTER_ON_OFF_CLUSTER_ID,
                               ATTR_ON_OFF_ATTRIBUTE_ID,
                               &onoff,
                               ATTR_BOOLEAN_ATTRIBUTE_TYPE);
    }

    //updata b attr
    tal_zg_write_attribute(1,
                           CLUSTER_LEVEL_CONTROL_CLUSTER_ID,
                           TY_ATTR_CURRENT_BRIGHT_ATTR_ID,
                           &bright,
                           ATTR_INT16U_ATTRIBUTE_TYPE);
}
/**
 * @description: __app_level_z3_cmd_move_to_level_handler
 * @param {cmd_type} 
 * @param {level} level 0 ~ 254
 * @param {trans_time_100ms}
 * @param {with_onoff} 
 * @return: none
 */
STATIC VOID_T __app_level_z3_cmd_move_to_level_handler(ZIGBEE_CMD_T cmd_type, UCHAR_T level, USHORT_T trans_time_100ms)
{
    TAL_SEND_QOS_E send_qos = QOS_0;
    UCHAR_T level_temp = level;
    USHORT_T ty_level = 0;
    USHORT_T transtime = 0;
    USHORT_T delay_ms = 0;

    TAL_PR_DEBUG("%s", __FUNCTION__);

    if (cmd_type == ZIGBEE_CMD_GROUP)
    {
        delay_ms = 10000 + tal_system_get_random(1000);
        send_qos = QOS_0;
    }
    /*if (trans_time_100ms == 10)
    {
        trans_time_100ms = 0;
    }*/
    transtime = trans_time_100ms;
    app_light_tools_zig_bt_2_app_bt(level_temp, 0, &ty_level, NULL);
    if (ty_level < CTRL_CW_BRIGHT_VALUE_MIN)
    {
        ty_level = CTRL_CW_BRIGHT_VALUE_MIN;
    }
    
    OPERATE_RET op_ret = app_light_ctrl_data_bright_set(ty_level, trans_time_100ms);
    if (op_ret != OPRT_OK)
    {
        TAL_PR_NOTICE("Zigbee Level set no effect");
        return;
    }

    app_handle_start(TRUE);

    UINT8_T onoff = app_light_ctrl_data_switch_get();
    tal_zg_write_attribute(1,
                           CLUSTER_ON_OFF_CLUSTER_ID,
                           ATTR_ON_OFF_ATTRIBUTE_ID,
                           &onoff,
                           ATTR_BOOLEAN_ATTRIBUTE_TYPE);

    tal_zg_write_attribute(1,
                           CLUSTER_LEVEL_CONTROL_CLUSTER_ID,
                           TY_ATTR_CURRENT_BRIGHT_ATTR_ID,
                           &ty_level,
                           ATTR_INT16U_ATTRIBUTE_TYPE);

    tal_zg_write_attribute(1,
                           CLUSTER_LEVEL_CONTROL_CLUSTER_ID,
                           ATTR_CURRENT_LEVEL_ATTRIBUTE_ID,
                           &level_temp,
                           ATTR_INT8U_ATTRIBUTE_TYPE);
    app_level_report_ty_bright_value(send_qos, delay_ms, ty_level);
}
/**
 * @description: Set a level value
 * @param {IN UCHAR_T level} level 0 ~ 254
 * @param {IN USHORT_T trans_time_100ms} delay_time value 
 * @param {IN UINT8_T with_onoff} affect the switch or not
 * @return: none
 */
STATIC VOID_T __app_level_cmd_move_to_level_handler(UCHAR_T level, USHORT_T trans_time_100ms)
{
    TAL_SEND_QOS_E send_qos = QOS_VIP_0;
    ULONG_T delay_ms = 0;
    UCHAR_T level_temp = level;
    USHORT_T ty_level = 0;

    TAL_PR_DEBUG("%s", __FUNCTION__);
    app_light_tools_zig_bt_2_app_bt(level_temp, 0, &ty_level, NULL);

    if (ty_level < CTRL_CW_BRIGHT_VALUE_MIN)
    {
        //The minimum white light brightness value is 10, otherwise the report will be converted to 0%
        ty_level = CTRL_CW_BRIGHT_VALUE_MIN;
    }

    OPERATE_RET op_ret = app_light_ctrl_data_bright_set(ty_level, trans_time_100ms);
    if (op_ret != OPRT_OK)
    {
        TAL_PR_DEBUG("Zigbee Level set no effect");
        return;
    }
    
    app_handle_start(TRUE);

    UINT8_T onoff = app_light_ctrl_data_switch_get();

    //updata onoff attribute
    tal_zg_write_attribute(1,
                           CLUSTER_ON_OFF_CLUSTER_ID,
                           ATTR_ON_OFF_ATTRIBUTE_ID,
                           &onoff,
                           ATTR_BOOLEAN_ATTRIBUTE_TYPE);

    app_onoff_report_value(send_qos, delay_ms, onoff);

    tal_zg_write_attribute(1,
                           CLUSTER_LEVEL_CONTROL_CLUSTER_ID,
                           ATTR_CURRENT_LEVEL_ATTRIBUTE_ID,
                           &level,
                           ATTR_INT8U_ATTRIBUTE_TYPE);

    //updata white light level attribute
    tal_zg_write_attribute(1,
                           CLUSTER_LEVEL_CONTROL_CLUSTER_ID,
                           TY_ATTR_CURRENT_BRIGHT_ATTR_ID,
                           &ty_level,
                           ATTR_INT16U_ATTRIBUTE_TYPE);
    app_level_report_ty_bright_value(send_qos, delay_ms, ty_level);
}
/**
 * @description: Step adjustment brightness command processing
 * @param {IN UCHAR_T step_mode} 0锛歋tep increase; 1锛歋tep reduction
 * @param {IN UCHAR_T step_size} step value
 * @param {IN USHORT_T trans_time_100ms} delay_time value
 * @param {IN UINT8_T with_onoff} affect the switch or not
 * @return: none
 */
STATIC VOID_T __app_level_cmd_step_level_handler(UCHAR_T step_mode, UCHAR_T step_size, USHORT_T trans_time_100ms)
{
    UCHAR_T curr_zig_bright = 0;
    UCHAR_T new_zig_bright = 0;

    tal_zg_read_attribute(1,
                          CLUSTER_LEVEL_CONTROL_CLUSTER_ID,
                          ATTR_CURRENT_LEVEL_ATTRIBUTE_ID,
                          &curr_zig_bright,
                          sizeof(curr_zig_bright));

    if (step_mode == STEP_LEVEL_UP)
    {
        //up
        if (curr_zig_bright + step_size > ZIGBEE_WHITE_B_MAX)
        {
            new_zig_bright = ZIGBEE_WHITE_B_MAX;
        }
        else
        {
            new_zig_bright = curr_zig_bright + step_size;
        }
    }
    else
    {
        //down
        if ((curr_zig_bright - step_size) < 0)
        {
            new_zig_bright = 0;
        }
        else
        {
            new_zig_bright = curr_zig_bright - step_size;
        }
    }
    TAL_PR_DEBUG("new_zig_bright = %d, curr_zig_bright =%d, step_size=%d", new_zig_bright, curr_zig_bright, step_size);
    __app_level_cmd_move_to_level_handler(new_zig_bright, trans_time_100ms);
}
/**
 * @description: Bright and HSV->V move up or move down
 * @param {move_mode} level move mode :up / down
 * @param {Rate} move rate,if 0x00/0xFF: 1/ms, else Rate/S
 * @param {with_onoff} move mode :up / down
 * @return: none
 */
STATIC OPERATE_RET __app_level_move_control(UCHAR_T move_mode, USHORT_T Rate)
{
    USHORT_T set_level;
    USHORT_T diff;
    USHORT_T trans_time_100ms;
    USHORT_T move_rate = Rate;
    USHORT_T current_bright = app_light_ctrl_handle_data_bright_get();

    app_light_ctrl_data_bright_get(&current_bright);

    if (Rate == 0 || Rate >= MAX_MOVE_RATE)
    {
        move_rate = MAX_MOVE_RATE;
    }

    if (move_mode == MOVE_UP)
    {
        set_level = 1000;
    }
    else
    {
        set_level = 10;
    }

    diff = (USHORT_T)app_light_tools_get_abs_value(set_level - current_bright);

    trans_time_100ms = diff * 10 / move_rate;
    OPERATE_RET op_ret = app_light_ctrl_data_bright_set(set_level, trans_time_100ms);
    if (op_ret != OPRT_OK)
    {
        TAL_PR_DEBUG("Zigbee Level set no effect");
        return OPRT_INVALID_PARM;
    }
    
    app_handle_start(TRUE);

    return OPRT_OK;
}
/**
 * @description: Stepless adjustment level value, if the onoff status changed, need to clear count down time and report attribute
 * @param {IN MOVE_MODE_T move_mode} Brighten/Darken
 * @param {IN USHORT_T Rate} Stepless adjustment speed, the unit is Rate/1000ms
 * @param {IN UINT8_T with_onoff} affect the switch or not
 * @return: none
 */
STATIC VOID_T __app_level_cmd_move_level_handler(UCHAR_T move_mode, USHORT_T Rate)
{
    UCHAR_T level;

    TAL_PR_DEBUG("%s", __FUNCTION__);

    if (move_mode == MOVE_UP)
    {
        level = 254;
    }
    else
    {
        //onoff the property is updated when the move is completed or stop
        level = 1;
    }

    tal_zg_write_attribute(1,
                           CLUSTER_LEVEL_CONTROL_CLUSTER_ID,
                           ATTR_CURRENT_LEVEL_ATTRIBUTE_ID,
                           &level,
                           ATTR_INT8U_ATTRIBUTE_TYPE);

    USHORT_T ty_rate = app_light_tools_zig_b_2_app_B(Rate);
    __app_level_move_control(move_mode, ty_rate);
}

/**
 * @note report level  attribute value
 * @param [in]{TAL_SEND_QOS_E} QOS value
 * @param [in]{USHORT_T} delay report
 * @param [in]{UCHAR_T} level
 * @return: none
 */
VOID_T app_level_report_level_value(TAL_SEND_QOS_E Qos, USHORT_T delay_ms, UCHAR_T level)
{
    TAL_ZG_SEND_DATA_T send_data;
    tal_system_memset(&send_data, 0, sizeof(TAL_ZG_SEND_DATA_T));

    send_data.qos = Qos;
    send_data.delay_time = delay_ms;
    send_data.zcl_id = ZCL_ID_BRIGHT;
    send_data.direction = ZG_ZCL_DATA_SERVER_TO_CLIENT;
    send_data.command_id = CMD_REPORT_ATTRIBUTES_COMMAND_ID;
    send_data.addr.mode = SEND_MODE_DEV;
    send_data.addr.type.dev.cluster_id = CLUSTER_LEVEL_CONTROL_CLUSTER_ID;
    send_data.addr.type.dev.src_ep = 1;
    send_data.addr.type.dev.dst_ep = 1;
    send_data.data.zg.attr_sum = 1;
    send_data.data.zg.attr[0].attr_id = ATTR_CURRENT_LEVEL_ATTRIBUTE_ID;
    send_data.data.zg.attr[0].type = ATTR_INT8U_ATTRIBUTE_TYPE;
    send_data.data.zg.attr[0].size = 1;
    send_data.data.zg.attr[0].value[0] = level;

    //tal_zg_clear_send_data(ZG_CLEAR_ALL_ZCL_ID, &send_data.zcl_id);
    tal_zg_send_data(&send_data, NULL, 1000);
    TAL_PR_DEBUG("Report level QOS:%d VALUE %d", Qos, level);
}
VOID_T rpt_light_cb(TAL_SEND_ST_E st, TAL_ZG_SEND_DATA_T *dcb)
{
    TAL_PR_DEBUG("Run Into>>>%s", __FUNCTION__);
    TAL_PR_DEBUG("ST:%d", st);
}
VOID_T app_level_report_ty_bright_value(TAL_SEND_QOS_E Qos, USHORT_T delay_ms, USHORT_T bright)
{
    TAL_ZG_SEND_DATA_T send_data;
    tal_system_memset(&send_data, 0, sizeof(TAL_ZG_SEND_DATA_T));
    send_data.qos = Qos;
    send_data.delay_time = delay_ms;
    send_data.zcl_id = ZCL_ID_BRIGHT;
    send_data.direction = ZG_ZCL_DATA_SERVER_TO_CLIENT;
    send_data.frame_type = ZG_ZCL_FRAME_TYPE_GLOBAL;
    send_data.command_id = CMD_REPORT_ATTRIBUTES_COMMAND_ID;

    send_data.addr.mode = SEND_MODE_DEV;
    send_data.addr.type.dev.dst_addr = TUYA_GATEWAY_ADDRESS;
    send_data.addr.type.dev.cluster_id = CLUSTER_LEVEL_CONTROL_CLUSTER_ID;
    send_data.addr.type.dev.src_ep = 1;
    send_data.addr.type.dev.dst_ep = 1;

    send_data.data.zg.attr_sum = 1;
    send_data.data.zg.attr[0].attr_id = TY_ATTR_CURRENT_BRIGHT_ATTR_ID;
    send_data.data.zg.attr[0].type = ATTR_INT16U_ATTRIBUTE_TYPE;
    send_data.data.zg.attr[0].size = 2;
    send_data.data.zg.attr[0].value[0] = (UCHAR_T)(bright);
    send_data.data.zg.attr[0].value[1] = (UCHAR_T)(bright >> 8);

    //tal_zg_clear_send_data(ZG_CLEAR_ALL_ZCL_ID, &send_data.zcl_id);
    tal_zg_send_data(&send_data, NULL, 1000);
    TAL_PR_DEBUG("Report level QOS:%d VALUE %d", Qos, bright);
}

/**
 * @brief 
 * 
 * @param cmd 
 * @param payload 
 * @param payload_len 
 * @param cmd_type 
 * @return OPERATE_RET 
 */
OPERATE_RET app_level_cluster_handler(UCHAR_T cmd, UINT8_T *payload, UINT8_T payload_len, ZIGBEE_CMD_T cmd_type)
{
    switch (cmd)
    {
    case CMD_MOVE_TO_LEVEL_COMMAND_ID:
    case CMD_MOVE_TO_LEVEL_WITH_ON_OFF_COMMAND_ID:
    {
        UCHAR_T level, option_mask = 0, option_override = 0;
        USHORT_T trans_time;
        level = payload[0];
        trans_time = payload[1] + (payload[2] << 8);

        if (payload_len == 5)
        {
            option_mask = payload[payload_len - 2];
            option_override = payload[payload_len - 1];
        }
        //option = app_zcl_get_option_value(CLUSTER_LEVEL_CONTROL_CLUSTER_ID, option_mask, option_override);
        __app_level_z3_cmd_move_to_level_handler(cmd_type, level, trans_time);
        break;
    }
    case TY_CMD_SET_BRIGHT_CMD_ID:
    {
        USHORT_T bright = (USHORT_T)payload[0] + (USHORT_T)(payload[1] << 8);
        __app_level_ty_cmd_set_bright_handler(cmd_type, bright);
        break;
    }
    case CMD_STEP_COMMAND_ID: //   don't effect switch
    case CMD_STEP_WITH_ON_OFF_COMMAND_ID:
    {
        //  effect switch
        UCHAR_T option_mask = 0, option_override = 0;
        UCHAR_T step_mode, step_size;
        USHORT_T trans_time;

        step_mode = payload[0];
        step_size = payload[1];
        trans_time = payload[2] + (payload[3] << 8);

        if (payload_len == 6)
        {
            option_mask = payload[payload_len - 2];
            option_override = payload[payload_len - 1];
        }
        //option = app_zcl_get_option_value(CLUSTER_LEVEL_CONTROL_CLUSTER_ID, option_mask, option_override);
        __app_level_cmd_step_level_handler(step_mode, step_size, trans_time);
        break;
    }

    case CMD_STOP_COMMAND_ID:
    case CMD_STOP_WITH_ON_OFF_COMMAND_ID:
    {

        app_light_ctrl_data_move_stop();
        break;
    }
    case CMD_MOVE_COMMAND_ID:
    case CMD_MOVE_WITH_ON_OFF_COMMAND_ID:
    {
        //move mode + rate
        UCHAR_T move_mode, option_mask = 0, option_override = 0;
        UCHAR_T move_rate;

        move_mode = payload[0];
        move_rate = payload[1];

        if (payload_len == 4)
        {
            option_mask = payload[payload_len - 2];
            option_override = payload[payload_len - 1];
        }
        //option = app_zcl_get_option_value(CLUSTER_LEVEL_CONTROL_CLUSTER_ID, option_mask, option_override);
        __app_level_cmd_move_level_handler(move_mode, move_rate);
        break;
    }
    default:
        break;
    }
    return OPRT_OK;
}
